/*
* Copyright (c) 2011, 2012 Roberto Tyley
*
* This file is part of 'Agit' - an Android Git client.
*
* Agit is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Agit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/ .
*/
package com.madgag.agit;
import static com.google.inject.util.Modules.override;
import static com.madgag.agit.GitTestUtils.DSA_USER;
import static com.madgag.agit.GitTestUtils.RSA_USER;
import static com.madgag.agit.GitTestUtils.integrationGitServerURIFor;
import static com.madgag.agit.git.Repos.remoteConfigFor;
import static com.madgag.agit.matchers.HasGitObjectMatcher.hasGitObject;
import static com.madgag.hamcrest.FileExistenceMatcher.exists;
import static com.madgag.hamcrest.FileLengthMatcher.ofLength;
import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static roboguice.RoboGuice.newDefaultRoboModule;
import android.app.Application;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.google.inject.Injector;
import com.madgag.agit.matchers.GitTestHelper;
import com.madgag.agit.operation.lifecycle.OperationLifecycleSupport;
import com.madgag.agit.operations.Clone;
import com.madgag.agit.operations.Fetch;
import com.madgag.agit.operations.GitAsyncTask;
import com.madgag.agit.operations.GitAsyncTaskFactory;
import com.madgag.agit.operations.GitOperation;
import com.madgag.agit.operations.OpNotification;
import com.madgag.agit.operations.Progress;
import com.madgag.agit.operations.Pull;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import roboguice.RoboGuice;
public class GitAsyncTaskTest extends ActivityInstrumentationTestCase2<DashboardActivity> {
private static final String TAG = "GitAsyncTaskTest";
private Injector injector;
public GitAsyncTaskTest() {
super("com.madgag.agit", DashboardActivity.class);
}
private GitTestHelper helper() {
return AndroidTestEnvironment.helper(getInstrumentation());
}
@Override
public void setUp() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Application application = (Application) getInstrumentation().getTargetContext().getApplicationContext();
injector = RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE,
newDefaultRoboModule(application),
override(new AgitModule()).with(new AgitIntegrationTestModule()));
}
@Override
public void tearDown() {
RoboGuice.util.reset();
}
@MediumTest
public void testCloneRepoWithEmptyBlobInPack() throws Exception {
Clone cloneOp = new Clone(true, integrationGitServerURIFor("tiny-repo.with-empty-file.git"),
helper().newFolder());
Repository repo = executeAndWaitFor(cloneOp);
assertThat(repo, hasGitObject("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); // empty blob
assertThat(repo, hasGitObject("adcb77d8f74590f54b4c1919b322aed456b22aeb")); // populated blob
}
@MediumTest
public void testCloneNonBareRepoFromLocalTestServer() throws Exception {
Clone cloneOp = new Clone(false, integrationGitServerURIFor("small-repo.early.git"), helper().newFolder());
Repository repo = executeAndWaitFor(cloneOp);
assertThat(repo, hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc"));
File readmeFile = new File(repo.getWorkTree(), "README");
assertThat(readmeFile, exists());
assertThat(readmeFile, ofLength(12));
}
@MediumTest
public void testFetchUpdatesOnCloneFromLocalTestServer() throws Exception {
Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-test-repo.early.git"), helper().newFolder());
Repository repository = executeAndWaitFor(cloneOp);
printFetchRefSpecs(repository);
String initialCommitId = "3974996807a9f596cf25ac3a714995c24bb97e2c", commit1 = "ce1e0703402e989bedf03d5df535401340f54b42";
assertThat(repository, hasGitObject(initialCommitId));
assertThat(repository.resolve("master").name(), equalTo(initialCommitId));
assertThat(repository, not(hasGitObject(commit1)));
setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git"));
executeAndWaitFor(new Fetch(repository, DEFAULT_REMOTE_NAME));
assertThat(repository, hasGitObject(commit1));
assertThat(repository.resolve("master").name(), equalTo(commit1));
}
private void printFetchRefSpecs(Repository repository) {
RemoteConfig remoteConfig = remoteConfigFor(repository, DEFAULT_REMOTE_NAME);
for (RefSpec refSpec : remoteConfig.getFetchRefSpecs()) {
Log.i(TAG, "refSpec = " + refSpec);
}
}
@MediumTest
public void testFetchUpdatesFromLocalTestServer() throws Exception {
Repository repository = helper().unpackRepo("small-test-repo.early.bare.git.zap");
setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git"));
String initialCommitId = "3974996807a9f596cf25ac3a714995c24bb97e2c", commit1 = "ce1e0703402e989bedf03d5df535401340f54b42";
assertThat(repository, hasGitObject(initialCommitId));
assertThat(repository.resolve("master").name(), equalTo(initialCommitId));
assertThat(repository, not(hasGitObject(commit1)));
executeAndWaitFor(new Fetch(repository, DEFAULT_REMOTE_NAME));
assertThat(repository, hasGitObject(commit1));
assertThat(repository.resolve("master").name(), equalTo(commit1));
}
@MediumTest
public void testPullUpdatesFromLocalTestServer() throws Exception {
Repository repository = helper().unpackRepo("small-test-repo.early.zap");
setRemoteUrl(repository, integrationGitServerURIFor("small-test-repo.later.git"));
// Git.wrap(repository).branchCreate().setName("master").setStartPoint("origin/master");
assertThat(repository, hasGitObject("3974996807a9f596cf25ac3a714995c24bb97e2c"));
String commit1 = "ce1e0703402e989bedf03d5df535401340f54b42";
assertThat(repository, not(hasGitObject(commit1)));
assertFileLength(2, repository.getWorkTree(), "EXAMPLE");
executeAndWaitFor(new Pull(repository));
assertThat(repository, hasGitObject(commit1));
assertFileLength(4, repository.getWorkTree(), "EXAMPLE");
}
private void assertFileLength(int length, File workTree, String exampleFile) {
File readmeFile = new File(workTree, exampleFile);
assertThat(readmeFile, exists());
assertThat("len=" + readmeFile.length(), readmeFile, ofLength(length));
}
@MediumTest
public void testCloneRepoUsingRSA() throws Exception {
Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(RSA_USER),
helper().newFolder());
assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc"));
}
@MediumTest
public void testCloneRepoUsingDSA() throws Exception {
Clone cloneOp = new Clone(true, integrationGitServerURIFor("small-repo.early.git").setUser(DSA_USER),
helper().newFolder());
assertThat(executeAndWaitFor(cloneOp), hasGitObject("ba1f63e4430bff267d112b1e8afc1d6294db0ccc"));
}
@MediumTest
public void testSimpleReadOnlyCloneFromGitHub() throws Exception {
Clone cloneOp = new Clone(false, new URIish("git://github.com/agittest/small-project.git"),
helper().newFolder());
Repository repo = executeAndWaitFor(cloneOp);
assertThat(repo, hasGitObject("9e0b5e42b3e1c59bc83b55142a8c50dfae36b144"));
assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe")));
File readmeFile = new File(repo.getWorkTree(), "README");
assertThat(readmeFile, exists());
}
@MediumTest
public void testNonBareCloneFromRepoWithFiveMBBlobForIssue47() throws Exception {
Clone cloneOp = new Clone(false, new URIish("git://github.com/rtyley/five-mb-file-test-repo.git"),
helper().newFolder());
Repository repo = executeAndWaitFor(cloneOp);
assertThat(repo, hasGitObject("3995316735a53542acdf0d92e0b725fe296c0b49"));
assertThat(repo, not(hasGitObject("111111111111111111111111111111111111cafe")));
File bigFile = new File(repo.getWorkTree(), "5mb.zeros");
assertThat(bigFile, exists());
}
// @LargeTest
// public void testCanCloneAllSuggestedRepos() throws Exception {
// for (SuggestedRepo suggestedRepo : SUGGESTIONS) {
// Repository repo = executeAndWaitFor(new Clone(true, new URIish(suggestedRepo.getURI()), tempFolder()));
// Map<String,Ref> allRefs = repo.getAllRefs();
// assertThat("Refs for " + suggestedRepo + " @ " + repo, allRefs.size(), greaterThan(0));
// }
// }
private Repository executeAndWaitFor(final GitOperation operation)
throws InterruptedException, IOException {
final CountDownLatch latch = new CountDownLatch(1);
Log.d(TAG, "About to start " + operation);
new Thread() {
public void run() {
Looper.prepare();
Log.d(TAG, "In run method for " + operation);
GitAsyncTask task = injector.getInstance(GitAsyncTaskFactory.class).createTaskFor(operation,
new OperationLifecycleSupport() {
public void startedWith(OpNotification ongoingNotification) {
Log.i(TAG, "Started " + operation + " with " + ongoingNotification);
}
public void publish(Progress progress) {
}
public void error(OpNotification notification) {
Log.i(TAG, "Errored " + operation + " with " + notification);
}
public void success(OpNotification completionNotification) {
}
public void completed(OpNotification completionNotification) {
Log.i(TAG, "Completed " + operation + " with " + completionNotification);
latch.countDown();
}
});
task.execute();
Log.d(TAG, "Called execute() on task for " + operation);
Looper.loop();
}
}.start();
long startTime = currentTimeMillis();
Log.i(TAG, "Waiting for " + operation + " to complete - currentThread=" + currentThread());
// http://stackoverflow.com/questions/5497324/why-arent-java-util-concurrent-timeunit-types-greater-than
// -seconds-available-in
boolean timeout = !latch.await(7 * 60, SECONDS);
long duration = currentTimeMillis() - startTime;
Log.i(TAG, "Finished waiting - timeout=" + timeout + " duration=" + duration);
assertThat("Timeout for " + operation, timeout, is(false));
return FileRepositoryBuilder.create(operation.getGitDir());
}
private void setRemoteUrl(Repository repository, URIish uri) throws IOException {
RemoteConfig remoteConfig = remoteConfigFor(repository, DEFAULT_REMOTE_NAME);
for (URIish urIish : remoteConfig.getURIs()) {
remoteConfig.removeURI(urIish);
}
remoteConfig.addURI(uri);
remoteConfig.update(repository.getConfig());
repository.getConfig().save();
}
}